package com.wct.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import Ice.StringHolder;
import cusexception.AccountException;
import cusexception.DatabaseException;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;

import com.alibaba.fastjson.JSON;
import org.springframework.web.bind.annotation.RestController;
import serverinterfaces.InterfacesPrx;

@RestController
@RequestMapping("/web")
@ConfigurationProperties(prefix = "weixin")
@EnableConfigurationProperties
public class Md5VerifyController {
    @Autowired
    private InterfacesPrx proxy;

    public String getFromUserName() {
        return fromUserName;
    }

    public void setFromUserName(String fromUserName) {
        this.fromUserName = fromUserName;
    }

    private String fromUserName;

    private String xml;

    private final String bindedPrompt = "您已经绑定了手机号码，请先解绑才可以重新绑定其他手机号码";

    final Pattern bindPattern = Pattern.compile("^#(\\d{11})#(.*)$");

    @Value("${fuqianla.signKey}")
    private String signKey;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 接收付钱拉通知入口
     *
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping("/notify")
    public void handleNotify(HttpServletRequest request, HttpServletResponse response) throws IOException {

        String msg = request.getParameter("msg");
        String resMsg = "fail";
        if (!"".equals(msg) && msg != null) {
            //logger.info(String.format("接收付钱拉通知内容 msg\n%s", msg));
            // 随机返回成功失败、测试使用
            String[] str = {"SUCCESS", "SUCCESS", "SUCCESS", "FAIL", "SUCCESS"};
            int random = (int) (Math.random() * 5);
            resMsg = str[random];
        } else {
            String requestStr = readReqStr(request);
            //logger.info(String.format("接收付钱拉通知内容\n%s", requestStr));
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> objMap = (Map<String, Object>) JSON.parse(requestStr);
                boolean flag = verify(objMap);
                if (flag) {
                    proxy.webConfirmPay((String) objMap.get("order_no"), (String) objMap.get("charge_id"), (String) objMap.get("ret_info"),
                            ((String) objMap.get("ret_code")).equals("0000") ? true : false, (String) objMap.get("receive_time"), (String) objMap.get("complete_time"));
                    resMsg = "success";
                }
            } catch (Exception e) {
                resMsg = "fail";
            }
        }
        //logger.info("接收付钱拉通知后响应信息,"+resMsg);
        response.setContentType("text/html");
        response.getWriter().write(resMsg);

    }

    /**
     * 请求转成string
     *
     * @param request
     * @return
     */
    public static String readReqStr(HttpServletRequest request) {
        BufferedReader reader = null;
        StringBuilder sb = new StringBuilder();
        try {
            reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "utf-8"));
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e);
        } finally {
            try {
                if (null != reader) {
                    reader.close();
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

    /**
     * 验证接收付钱拉通知签名入口
     *
     * @param objMap
     * @return
     * @throws Exception
     */
    public boolean verify(Map<String, ?> objMap) {
        boolean flag = false;
        try {
            String hexSign = String.valueOf(objMap.get("sign_info"));
            // 得到待签名数据
            Map<String, ?> filterMap = paraFilter(objMap);
            String linkStr = createLinkString(filterMap);
            // 拼装md5串  md5.linkStr
            String templinkStr = linkStr + "&key=" + signKey;
            String md5Hex = DigestUtils.md5Hex(templinkStr.getBytes("UTF-8"));
            // 验证签名后数据是否相同
            flag = hexSign.equalsIgnoreCase(md5Hex);
        } catch (Exception e) {
            //验证通知签名信息error
        }
        return flag;

    }

    /**
     * 除去数组中的空值和签名参数
     *
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public static Map<String, ?> paraFilter(Map<String, ?> sArray) {
        Map<String, Object> result = new HashMap<String, Object>();
        if ((sArray == null) || (sArray.size() <= 0)) {
            return result;
        }
        for (String key : sArray.keySet()) {
            Object value = sArray.get(key);
            if ((value == null) || value.equals("") || key.equalsIgnoreCase("sign_info")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            if (value instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, ?> m = (Map<String, ?>) value;
                result.put(key, paraFilter(m));
            } else if (value instanceof List) {
                continue;// 不应包含多集合数据
            } else {
                result.put(key, value);
            }
        }
        return result;
    }

    /**
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     *
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, ?> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        StringBuffer prestr = new StringBuffer("");
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            Object o = params.get(key);
            String value = String.valueOf(o);
            if (o instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, ?> m = (Map<String, ?>) o;
                value = "{" + createLinkString(m) + "}";
            }

            if (i == (keys.size() - 1)) {// 拼接时，不包括最后一个&字符
                prestr.append(key + "=" + value);
            } else {
                prestr.append(key + "=" + value + "&");
            }
        }
        return prestr.toString();
    }

    @RequestMapping("/weixin")
    public void weixin(HttpServletRequest request, HttpServletResponse response) throws IOException, DatabaseException, DocumentException {
        if (request.getMethod().equals("GET")) {
            String msg = request.getParameter("echostr");
            response.setContentType("text/plain");
            response.getWriter().write(msg);
        } else {
            InputStream inputStream = request.getInputStream();
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(inputStream);
            Node ueridNode = document.selectSingleNode("//FromUserName");
            String userid = ueridNode.getText();
            StringHolder deviceId = new StringHolder();
            Boolean isBind = false;

            isBind = proxy.isBind(userid, deviceId);


            Node keyNode = document.selectSingleNode("//EventKey");
            if (keyNode != null) {
                String key = keyNode.getText();
                handldMenuClickEvent(key, userid, deviceId.value, isBind);
            } else {

                Node contentNode = document.selectSingleNode("//Content");
                String content = contentNode.getText();
                handleTextMessage(content, userid, deviceId.value, isBind);

            }

            if (xml!=null) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("text/xml;charset=UTF-8");
                response.getWriter().write(xml);
            }
            else {
                response.setContentType("text/plain");
                response.getWriter().write("success");
            }

        }
    }

    private void makeXMLResponse(String userid, String content) {
        Element root = DocumentHelper.createElement("xml");

        root.addElement("ToUserName").addCDATA(userid);

        root.addElement("FromUserName").addCDATA(fromUserName);
        root.addElement("CreateTime").addText((new Date().getTime() / 1000) + "");
        root.addElement("MsgType").addCDATA("text");
        root.addElement("Content").addCDATA(content);

        xml = root.asXML();
    }

    private void handldMenuClickEvent(String key, String userid, String deviceId, Boolean isBind) throws DocumentException {

        switch (key) {
            case "B_BIND":
                if (isBind) {
                    makeXMLResponse(userid, bindedPrompt);
                } else {
                    makeWelcomeMessage(userid);
                }

                break;

            case "B_UNBIND":
                if (isBind) {
                    makeXMLResponse(userid, "请确认您要解除绑定吗？确认请回复【确认解绑】,否则请忽略");
                } else {
                    makeWelcomeMessage(userid);
                }

                break;

            default:
                break;
        }
    }

    private void makeWelcomeMessage(String userId) {
        makeXMLResponse(userId, "欢迎使用鹰眼手机防盗定位，您还没有绑定手机号码，请发送以下格式的消息完成绑定：\n#手机号码#密码");
    }

    private void handleTextMessage(String content, String userid, String deviceId, Boolean isBind) throws DatabaseException {
        if (processBindMessage(content, userid, deviceId, isBind)) {

        } else if (processUnBindMessage(content, userid, deviceId, isBind)) {

        }
        else{
            makeXMLResponse(userid, "无法识别的命令，请使用菜单上并根据提示操作");
        }
    }

    private Boolean processBindMessage(String content, String userid, String deviceId, Boolean isBind) {

        Matcher matcher = bindPattern.matcher(content);
        if (matcher.find()) {
            String name = matcher.group(1);
            String pass = matcher.group(2);
            if (isBind) {
                makeXMLResponse(userid, bindedPrompt);
            } else {
                try {
                    proxy.bind(userid, name, pass);
                    makeXMLResponse(userid, "恭喜绑定成功！");
                } catch (DatabaseException e) {
                    e.printStackTrace();
                } catch (AccountException e) {
                    if (e.errorMessage.equals("电话号码或密码错误")) {
                        makeXMLResponse(userid, "电话号码或密码错误，绑定失败!\n请发送以下格式的消息完成绑定：\n#手机号码#密码");
                    }
                }
            }
            return true;
        } else {
            return false;
        }

    }

    private Boolean processUnBindMessage(String content, String userid, String deviceId, Boolean isBind) {
        if (content.equals("确认解绑")) {
            try {
                if (isBind) {
                    proxy.unBind(userid);
                    makeXMLResponse(userid, "解除绑定成功");
                } else {
                    makeXMLResponse(userid, "您还没有绑定手机号码");
                }

            } catch (DatabaseException e) {

                makeXMLResponse(userid, e.errorMessage);

            }
            return true;
        } else
            return false;
    }
}
